--[[

    Filename:	utility.lua

    Programmer:	aesiot

    Remark:

--]]

local auxenv = require( "auxenv" )
local argchk = require( "argchk" )

local utility = { _VERSION = "1.0" }

--------------------------------------------------------------------------------

local _MAIN_START_TAG = nil

function utility.task_entry_start()

	if _MAIN_START_TAG then

		return
	end

	auxmsg.bind( "ping", function()

		return true
	end )

	auxmsg.bind( "kill", function()

		printf( 4, "[ TASK ] KILL TASK (%s)", vm.get_task_name() )

		auxenv.end_task()

		return true
	end )

	auxtmr.create( nil, 0, 0, function()

		if auxlib.is_function( main ) then

			auxthr.run( main, function( r, m )

				if not r then

					auxenv.print_runtime_error( m )
				end

				printf( 4, "[ TASK ] END OF TASK (%s)", vm.get_task_name() )

				if auxlib.is_function( atexit ) then

					atexit()
				else

					auxenv.end_task()
				end
			end )
		else

			printf( 3, "[ TASK ] MISSING ENTRY POINT (%s)", vm.get_task_name() )

			if auxlib.is_function( atexit ) then

				atexit()
			else

				auxenv.end_task()
			end
		end
	end )

	_MAIN_START_TAG = null
end

--------------------------------------------------------------------------------

function utility.ltui_entry_start()

	if _MAIN_START_TAG then

		return
	end

	local ltui = require( "ltui" )

	auxtmr.create( nil, 0, 0, function()

		if auxlib.is_function( main ) then

			coroutine.resume( coroutine.create( function()

				local application = ltui.application

				local app = application()

				function app:init()

					application.init( self, "luavm.app" )

					ltui_app = self

					main( self )
				end

				auxlib.pcall( app.run, app )

				if auxlib.is_function( atexit ) then

					atexit()
				else

					auxenv.end_task()
				end
			end ) )
		else

			printf( 3, "[ TASK ] MISSING ENTRY POINT (%s)", vm.get_task_name() )

			if auxlib.is_function( atexit ) then

				atexit()
			else

				auxenv.end_task()
			end
		end
	end )

	_MAIN_START_TAG = null
end

--------------------------------------------------------------------------------

local function gen_aes_decrypt( key )

	local Crypto = require( "Crypto" )

	local aesdec = auxlib.assert( Crypto.new_aes( "decrypt", key ) )

	local function padding_pkcs7_dec( data )

		if not auxlib.is_string( data ) then

			return nil
		end

		local len = string.len( data )

		return string.sub( data, 1, len - string.byte( data, len ) )
	end

	return function( data )

		return padding_pkcs7_dec( aesdec:ecb_dec( data ) )
	end
end

local function gen_aes_encrypt( key )

	local Crypto = require( "Crypto" )

	local aesenc = auxlib.assert( Crypto.new_aes( "encrypt", key ) )

	local function padding_pkcs7_enc( data, round )

		if not auxlib.is_string( data ) then

			return nil
		end

		local len = string.len( data )

		local num = round - ( len % round )

		return data .. string.rep( string.char( num ), num )
	end

	return function( data )

		return aesenc:ecb_enc( padding_pkcs7_enc( data, 16 ) )
	end
end

function utility.gen_aes_crypto( key )

	argchk.check_s( key, 1 )

	return {

		decrypt = gen_aes_decrypt( key ),
		encrypt = gen_aes_encrypt( key )
	}
end

--------------------------------------------------------------------------------

function utility.gen_local_serialize( filename, crypto, rootdir )

	argchk.check_s( filename, 1, false )
	argchk.check_t( crypto,   2, true  )
	argchk.check_s( rootdir,  3, true  )

	local JSON = require( "JSON" )

	local pathname = string.format( "%s/%s", ( rootdir or auxenv.pathname ), filename )

	do
		local path = auxenv.path_split( pathname )

		if not auxlib.is_directory( path ) then

			local cmdfmt = auxlib.if_else( "Windows" == os.getenv( "OSName" ), 'mkdir "%s"', 'mkdir -p "%s"' )

			os.execute( string.format( cmdfmt, path ) )
		end
	end

	return {

		pack = function( data )

			local json = JSON.encode( data )

			if crypto and crypto.encrypt then

				json = crypto.encrypt( json )
			end

			local file = io.open( pathname, "wb" )

			if nil == file then

				return false
			end

			file:write( json )

			file:close()

			return true
		end,

		unpack = function()

			local file = io.open( pathname, "rb" )

			if nil == file then

				return nil
			end

			local text = file:read( 'a' )

			file:close()

			if crypto and crypto.decrypt then

				text = crypto.decrypt( text )
			end

			return JSON.decode( text )
		end
	}
end

--------------------------------------------------------------------------------

function utility.create_watchdog( name, path, once, vmname )

	argchk.check_s( name,   1, true )
	argchk.check_s( path,   2, true )
	argchk.check_b( once,   3, true )
	argchk.check_s( vmname, 4, true )

	if auxlib.is_none( name ) then

		name = vm.get_task_name()
	end

	if auxlib.is_none( path ) then

		path = arg[ 1 ]
	end

	if auxlib.is_none( vmname ) then

		vmname = string.format( "watchdog(%s)", name )
	end

	auxlib.assert( auxlib.is_regular_file( path ) )

	return auxenv.task_create( function()

		os.remove( arg[ 1 ] )

		local auxenv = require( "auxenv" )

		local name = arg[ 2 ]
		local path = arg[ 3 ]
		local once = arg[ 4 ]

		auxtmr.create( nil, 0, 5000, function()

			local exist = false

			for k, v in pairs( vm.get_task_list() ) do

				if v == name then

					exist = true

					break
				end
			end

			if not exist then

				if vm.task_create( name, path ) then

					logger.i( string.format( "STARTUP MODULE %s", name ) )

					if "true" == once then

						auxenv.end_task()
					end
				end
			end
		end )

	end, vmname, name, path, tostring( once ) )
end

--------------------------------------------------------------------------------

function utility.execute( retprc, cmdtxt, vmname, method )

	argchk.check_f( retprc, 1, true  )
	argchk.check_s( cmdtxt, 2, false )
	argchk.check_s( vmname, 3, true  )
	argchk.check_s( method, 4, true  )

	if auxlib.is_none( vmname ) then

		vmname = string.format( "shellrun(%s)", cmdtxt )
	end

	if auxlib.is_none( method ) then

		method = string.format( "callback(%s)", retprc )
	end

	auxlib.assert( not auxmsg.exist( method ) )

	if retprc then

		auxmsg.bind( method, function( ... )

			auxmsg.bind( method, nil )

			retprc( ... )
		end )
	end

	if auxenv.task_create( function()

		os.remove( arg[ 1 ] )

		local auxenv = require( "auxenv" )

		local b, s, c = os.execute( arg[ 2 ] )

		pcall( auxmsg.send, arg[ 3 ], nil, arg[ 4 ], b, s, c )

		auxenv.end_task()

	end, vmname, cmdtxt, vm.get_task_name(), method ) then

		return true
	end

	auxmsg.bind( method, nil )

	return false
end

--------------------------------------------------------------------------------

return utility
